package cn.bugstack.springframework.beans.factory.xml;

import cn.bugstack.springframework.beans.BeansException;
import cn.bugstack.springframework.beans.PropertyValue;
import cn.bugstack.springframework.core.io.Resource;
import cn.bugstack.springframework.core.io.ResourceLoader;
import cn.bugstack.springframework.beans.factory.config.BeanDefinition;
import cn.bugstack.springframework.beans.factory.config.BeanReference;
import cn.bugstack.springframework.beans.factory.support.AbstractBeanDefinitionReader;
import cn.bugstack.springframework.beans.factory.support.BeanDefinitionRegistry;
import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.XmlUtil;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;

import java.io.IOException;
import java.io.InputStream;

/**
 * @param
 * @author oyz
 * @version 1.0
 * @description: TODO 解析xml处理bean的注册
 * @date 2022/3/22 21:58
 */
public class XmlBeanDefinintionReader extends AbstractBeanDefinitionReader {


    public XmlBeanDefinintionReader(BeanDefinitionRegistry beanDefinitionRegistry) {
        super(beanDefinitionRegistry);
    }

    public XmlBeanDefinintionReader(BeanDefinitionRegistry registry, ResourceLoader loader) {
        super(registry, loader);
    }

    @Override
    public void loadBeanDefinitions(Resource resource) throws BeansException {
        try {
            InputStream inputStream = resource.getInputStream();
            doLoadBeanDefinitions(inputStream);
        } catch (IOException | ClassNotFoundException e) {
            throw new BeansException("IOException parsing XML document from "+resource,e);
        }
    }

    @Override
    public void loadBeanDefinitions(Resource... resources) throws BeansException {
        for (Resource resource : resources) {
            loadBeanDefinitions(resource);
        }
    }

    @Override
    public void loadBeanDefinitions(String location) throws BeansException {
        ResourceLoader resourceLoader = getResourceLoader();
        Resource resource = resourceLoader.getResource(location);
        loadBeanDefinitions(resource);
    }

    private void doLoadBeanDefinitions(InputStream inputStream) throws ClassNotFoundException, BeansException {
        Document document = XmlUtil.readXML(inputStream);
        Element root = document.getDocumentElement();
        NodeList childNodes = root.getChildNodes();
        for (int i = 0; i < childNodes.getLength(); i++) {
            // 判断元素
            if(!(childNodes.item(i) instanceof Element)) continue;
            // 判断对象 <bean id = "student" class="com.oyz.student" />
            if(!"bean".equals(childNodes.item(i).getNodeName())) continue;
            // 解析标签
            Element bean  = (Element) childNodes.item(i);
            String id = bean.getAttribute("id");
            String className = bean.getAttribute("class");
            String name = bean.getAttribute("name");
            // 开始使用反射
            Class<?> clazz = Class.forName(className);
            // 优先级 id>name
            String beanName = StrUtil.isNotBlank(id) ? id : name;
            if (StrUtil.isNotBlank(beanName)){
                beanName = StrUtil.lowerFirst(beanName);
            }
            // 定义bean
            BeanDefinition beanDefinition = new BeanDefinition(clazz);
            // 进行属性填充
            for (int j = 0; j < bean.getChildNodes().getLength(); j++) {
                if(!(bean.getChildNodes().item(j) instanceof Element)) continue;
                if(!"property".equals(bean.getChildNodes().item(i).getNodeName())) continue;
                //  解析 <property name ="" value= "" ref = ""/>
                Element property = (Element) bean.getChildNodes().item(j);
                String attrName = property.getAttribute("name");
                String attrValue = property.getAttribute("value");
                String attrRef = property.getAttribute("ref");
                // 获取引用对象
                Object value = StrUtil.isNotBlank(attrRef) ? new BeanReference(attrRef) : attrValue;
                // 创建属性信息
                PropertyValue propertyValue = new PropertyValue(attrName,value);
                // 一个类可能会有多个属性
                beanDefinition.getPropertyValues().addPropertyValue(propertyValue);
            }

            // 判断该bean是否已经被注册
            if(getRegistry().containsBeanDefinition(beanName)){
                throw new BeansException("Duplicate beanName["+ beanName  +"] is not be allowed");
            }
            // 开始注册ioc的逻辑
            getRegistry().registerBeanDefinition(beanName,beanDefinition);
        }
    }
}
